#  main.py
#  pygametest
#
#  Created by Zack Schilling on 12/9/08.
#  Copyright (c) 2008 Dangerous Wares. All rights reserved.
#

#Import Programming Modules
import os, pygame, array, math, numpy
from pygame.locals import *
from pygame import bufferproxy
from numpy import *

#Import the objects we need to get started
from Game import *
from Controller import *
from HUD import *
from Menus import *
from NamedObjects import NamedObjects
from PhysicsObject import *
from Player import *
from Triggers import *
from AnimatedTile import *
from LevelObjects import *

# Load up our helper functions
from Loaders import *

if not pygame.font: print 'Warning, fonts disabled'
if not pygame.mixer: print 'Warning, sound disabled'
    
class RhythmEngine:
    def __init__(self, gameinfo):
        self.gameinfo = gameinfo
        self.powermeter = gameinfo.powermeter
        self.controller = gameinfo.powermeter
        self.player = gameinfo.player
        self.songname = gameinfo.music
        
        self.rhythmcount = 0
        self.rhythmtracklength = -1
        self.rhythmtrack = list()
        
        self.rhythmcount2 = 0
        self.rhythmtracklength2 = -1
        self.rhythmtrack2 = list()
        
        self.activebeats = list()
        self.surf = pygame.display.get_surface()
        
        self.duration = 500
        self.startx = 0
        self.stopx = 0
        self.y = 0
        
        self.beatimg = (load_frame(os.path.join('data','interface', 'lowbeat.png')),load_frame(os.path.join('data','interface', 'highbeat.png')))
        #self.beatstrip = pygame.Surface((self.gameinfo.screenx,27))
        #self.beatstrip.fill((255,255,255))
        
    def load_song(self, name):
        # Reset some internal variables
        self.songname = name
        self.rhythmtrack = list()
        self.rhythmcount = 0
        self.rhythmcount2 = 0
        self.rhythmtrack2 = list()
        self.activebeats = list()
        
        # Setup the mixer with the song.
        pygame.mixer.music.load(os.path.join('data','music', name +'.ogg'))
        
        # Open the high rhythm track
        file = open(os.path.join('data','music', name + '-high.txt'), 'r')
        beats = file.read()
        beats = beats.split('\n')
        for beat in beats:
            if beat != "":
                self.rhythmtrack.append(int(beat))
        self.rhythmtracklength = len(self.rhythmtrack);
        
        # Open the low rhythm track
        file = open(os.path.join('data','music', name + '-low.txt'), 'r')
        beats = file.read()
        beats = beats.split('\n')
        for beat in beats:
            if beat != "":
                self.rhythmtrack2.append(int(beat))
        self.rhythmtracklength2 = len(self.rhythmtrack2);
        
    def play(self):
        pygame.mixer.music.play(-1)
    def pause(self):
        pygame.mixer.music.pause()
    def unpause(self):
        pygame.mixer.music.unpause()
    def reset(self):
        pass
    def update(self):
        musicpos = pygame.mixer.music.get_pos()
        # Look to see if any upcoming beats are within self.duration seconds of play
        while (self.rhythmcount < self.rhythmtracklength) and (self.rhythmtrack[self.rhythmcount] - musicpos < self.duration):
            self.activebeats.append((self.rhythmtrack[self.rhythmcount],1))
            self.rhythmcount += 1;
            
            
        #while (self.rhythmcount2 < self.rhythmtracklength2) and (self.rhythmtrack2[self.rhythmcount2] - musicpos < self.duration):
            #self.activebeats.append((self.rhythmtrack2[self.rhythmcount2],0))
            #self.rhythmcount2 += 1;
        
        # Draw the backdrop for the rhythm strip.
        #self.surf.blit(self.beatstrip,(0,self.y-1))
        
        # Update the positioning of the bar based on player location
        self.startx = self.player.rect.centerx + 100
        #self.duration = 2000
        self.stopx = self.player.rect.centerx
        self.y = self.player.rect.centery - 18

        # Run through all the active beats
        for beat in self.activebeats:
            # Draw them
            #self.surf.blit(self.beatimg[beat[1]],((self.startx-self.stopx)*(float(beat[0] - musicpos)/self.duration)+self.stopx,self.y))
            # Kill the dead ones
            if (beat[0] - musicpos) < -200:
                self.activebeats.remove(beat)
                #self.powermeter.deltalevel(1)
            
        # Check to see if the controller just pressed a key
        # Check to see if this is a beat frame

# This class manages the control configuration 
class GameInfo:
    """A class that holds information about the current level and screen size."""
    def __init__(self):
        
        self.load_conf()
        
        # Initialize the screen
        if self.fullscreen == 1 and self.doublebuffer == 1:
            self.screen = pygame.display.set_mode((self.screenx, self.screeny), pygame.DOUBLEBUF|pygame.FULLSCREEN)
        elif self.fullscreen == 0 and self.doublebuffer == 1:
            self.screen = pygame.display.set_mode((self.screenx, self.screeny), pygame.DOUBLEBUF)
        elif self.fullscreen == 1 and self.doublebuffer == 0:
            self.screen = pygame.display.set_mode((self.screenx, self.screeny), pygame.FULLSCREEN)
        else:
            self.screen = pygame.display.set_mode((self.screenx, self.screeny))
        pygame.mouse.set_visible(0)
        
        # Graphical Data
        self.font = pygame.font.Font(os.path.join('data','freesansbold.ttf'), 36)
        self.title = pygame.font.Font(os.path.join('data','freesansbold.ttf'), 72)
        self.subtitle = pygame.font.Font(os.path.join('data','freesansbold.ttf'), 48)
        self.item = pygame.font.Font(os.path.join('data','freesansbold.ttf'), 32)
        
        # Setup the HUD Objects
        self.hudicon = HUDIcon(self)
        self.livesmeter = LivesMeter(self)
        
        # Setup the input devices
        self.controller = Controller()
    
    def setblock(self,rect):
        self.hudicon.show(1)
        self.camera.setblock(rect)
        self.blockedobjects.empty()
        self.blockedobjects.add(self.player)
        self.block = rect
        self.blocking = True
        #print "Enabled Blocking"
        
    def clearblock(self):
        self.hudicon.show(0)
        self.camera.clearblock()
        self.blockedobjects.empty()
        self.blocking = False
        #print "Disabled Blocking"
        
    def load_conf(self):
    
        # Settings
        self.screenx = 800
        self.screeny = 500
        self.startx = 100
        self.starty = 900
        self.fullscreen = 0
        self.doublebuffer = 1
        self.levelname = "vida"
        self.music = "cog"
        self.framerate = 60
        self.lag = 0
    
        cfgfile = open(os.path.join('data',"conf.txt"))
        str = cfgfile.read()
        str = str.split('\n')
        
        for line in str:
            setter = line.split('=')
            if setter[0] == "width":
                self.screenx = int(setter[1])
            elif setter[0] == "height":
                self.screeny = int(setter[1])
            elif setter[0] == "fullscreen":
                self.fullscreen = int(setter[1])
            elif setter[0] == "doublebuffer":
                self.doublebuffer = int(setter[1])
            elif setter[0] == "level":
                self.levelname = setter[1]
            elif setter[0] == "framerate":
                self.framerate = int(setter[1])
            elif setter[0] == "training":
                self.training = int(setter[1])
            elif setter[0] == "lag":
                self.lag = int(setter[1])
            elif setter[0] == "enablelives":
                self.enablelives = int(setter[1])
        
    def setup_vars(self):
        # Game Object Groups (Organizational)
        self.physicsobjects = pygame.sprite.Group()     # All in-play PhysicsObjects
        self.blockedobjects = pygame.sprite.Group()     # All in-play PhysicsObjects trapped in a block event
        
        # Game Object Groups (Attack)
        self.friendly = pygame.sprite.Group()
        self.unfriendly = pygame.sprite.Group()
        self.levelobjs = pygame.sprite.Group()
        
        # Game Object Groups (Non-Physics)
        self.animtiles = list()
        self.statictiles = list()
        self.animobjs = list()
        self.loadlevelobjs = list()
        
        self.allsprites = pygame.sprite.LayeredUpdates()
        
        # Game Logic (Triggers and Conditions)
        self.triggers = list()
        self.conditions = list()
        self.effects = list()
        self.namedobjects = NamedObjects()
        self.namedeffects = NamedObjects()
        self.namedconditions = NamedObjects()
        
        self.blocking = False
        self.block = None
        
        self.gameover = False
        self.gameovermenu = None
        self.enablelives = 1
        self.lives = 4
    
    def loadlevel(self):
        
        # Make sure the music is stopped
        pygame.mixer.music.stop()
        # No HUD icons are up
        self.hudicon.reset()
        
        # Setup the class variables
        self.setup_vars()
        
        # Setup a few variables for loading levels
        setuptriggers = list()
        
        cfgfile = open(os.path.join('data','levels',self.levelname,"props.txt"))
        str = cfgfile.read()
        str = str.split('\n')
        
        for line in str:
            setter = line.split('=')
            if setter[0] == "startx":
                self.startx = int(setter[1])
            elif setter[0] == "starty":
                self.starty = int(setter[1])
            elif setter[0] == "music":
                self.music = setter[1]
            
            # Load up static and animated tiles
            elif setter[0] == "statictile":
                newtile = [setter[1],"",0,0,1,1]
                self.statictiles.append(newtile)
            elif setter[0] == "animtile":
                newtile = [setter[1],"",0,0,1,1]
                self.animtiles.append(newtile)
            elif setter[0] == "layer":
                newtile[1] = int(setter[1])
            elif setter[0] == "x":
                newtile[2] = int(setter[1])
            elif setter[0] == "y":
                newtile[3] = int(setter[1])
            elif setter[0] == "repeatx":
                newtile[4] = int(setter[1])
            elif setter[0] == "repeaty":
                newtile[5] = int(setter[1])
                
            # Load up the level objects (spikes, lava, etc)
            elif setter[0] == "levelobj":
                self.loadlevelobjs.append(setter[1].split(','))
                
            # Load up Conditions
            elif setter[0] == "condition":
                newcondition = Condition()
                self.namedconditions.add(setter[1], newcondition)
                self.conditions.append(newcondition)
            elif setter[0] == "sticky":
                newcondition.setoneshot(setter[1])
            elif setter[0] == "alive":
                newcondition.setalive(setter[1])
            elif setter[0] == "dead":
                newcondition.setdead(setter[1])
            elif setter[0] == "position":
                newcondition.setposition(setter[1])
            elif setter[0] == "subconditions":
                newcondition.setsubconditions(setter[1])
                
            # Load up Effects
            elif setter[0] == "effect":
                neweffect = Effect()
                self.namedeffects.add(setter[1], neweffect)
                self.effects.append(neweffect)
            elif setter[0] == "kill":
                neweffect.setkill(setter[1])
            elif setter[0] == "spawn":
                neweffect.setspawn(setter[1])
            elif setter[0] == "block":
                neweffect.setblock(setter[1])
            elif setter[0] == "clearblock":
                neweffect.setclearblock(setter[1])
            elif setter[0] == "checkpoint":
                neweffect.setcheckpoint(setter[1])
            elif setter[0] == "victory":
                neweffect.setvictory(setter[1])
            
            # Load up Triggers
            elif setter[0] == "trigger":
                newtrigger = [setter[1],0,"",""]
                setuptriggers.append(newtrigger)
            elif setter[0] == "oneshot":
                newtrigger[1] = int(setter[1])
            elif setter[0] == "conditions":
                newtrigger[2] = setter[1]
            elif setter[0] == "effects":
                newtrigger[3] = setter[1]
    
        self.updateloading("Background Images...")
        
        # Load the background
        self.background = ScrollingBG(self)
        
        # Setup the physics module with the collision masks
        PhysicsObject.setup(self.background.mask,self.background.ground,self.background.fgimage)
        
        self.levelx = self.background.fgimage.rect.width
        self.levely = self.background.fgimage.rect.height
        self.player = Player((self.startx,self.starty), self.controller)
        self.camera = Camera(self)
        self.powermeter = PowerMeter(self)
        self.rhythmengine = RhythmEngine(self)
        pygame.display.set_caption('Unnamed Game, Level Code ' + self.levelname)
        
        # Setup the subconditions (in case some of them were below in the file)
        for condition in self.conditions:
            condition.setupsubconditions(self.namedconditions)
        
        # Load up the triggers
        for trigger in setuptriggers:
            newtrigger = Trigger(trigger[1],trigger[2],trigger[3],self)
            self.triggers.append(newtrigger)
        
        # Prepare the remaining game objects (the player, camera, and onscreen display)
        
        self.updateloading("Animated Tiles...")
        # Spawn in the level's animated tiles
        for anim in self.animtiles:
            
            # Peek into the tile config to get the first frame
            cfgfile = open(os.path.join('data','tiles', anim[0] + ".txt"))
            str = cfgfile.read()
            str = str.split('\n')
            
            for line in str:
                setter = line.split('=')
                if setter[0] == "frame":
                    stamp = load_frame(os.path.join('data','tiles', setter[1] + ".png"))
                    break
            
            for i in range(anim[4]):
                for j in range(anim[5]):
                    self.animobjs.append(AnimatedTile(anim[0],anim[1],anim[2]+(i*stamp.get_width()),anim[3]+(j*stamp.get_height())))
        
        self.updateloading("Spawning Characters...")
        
        # Spawn the Player object
        self.spawnobj(self.player,COMBAT_PLAYER)
        
        # Add in the level objects
        for args in self.loadlevelobjs:
            self.spawnobj(createobj(args,self))
            
        # Add in the paralax and background.
        self.allsprites.add(self.background.bdimage, self.background.bgimage,layer=-2)
            
        # Add in the animated objects
        for anim in self.animobjs:
            self.allsprites.add(anim,layer=anim.layer)
        
        # Add in the foreground and the HUD
        self.allsprites.add(self.background.fgimage,layer=1) #frametimer
        
        self.updateloading("Getting My Groove On...")
        #Begin playing the music
        self.rhythmengine.load_song(self.music)
        self.rhythmengine.play()

    def resetlevel(self):
        # Focus the camera back on the player
        self.camera.player = self.player
        # Clear all the physicsobjects that are not the player.
        for obj in self.physicsobjects:
            if obj != self.player:
                self.remove(obj)
        # Reset all triggers.
        for trigger in self.triggers:
            trigger.reset()
        # Reset any level blocking
        self.clearblock()
        self.hudicon.show(2,90)
        # Replace all the level objects
        for args in self.loadlevelobjs:
            self.spawnobj(createobj(args,self))
        
    def checkpoint(self, newspawn):
        # Set the new Spawnpoint
        self.player.spawnpoint = newspawn
        
        # Remove the one shot triggers that have already been tripped.
        for trigger in self.triggers:
            if trigger.oneshot and trigger.triggered:
                self.triggers.remove(trigger)

        # Display the HUD Icon that lets the player know this has occured.
        self.hudicon.show(3,90)
        
    def spawnobj(self,obj,type=None,renderlayer=0):
        if type == None:
            type = obj.type
        if type == None:
            type = COMBAT_ENEMY

        self.physicsobjects.add(obj)
        self.allsprites.add(obj,layer=renderlayer)
        
        # If the object has a name, add it to the named objects
        if obj.name != "":
            self.namedobjects.add(obj.name,obj)
        
        # Assign the object a hit group (for combat)
        if type[0][0]:
            self.friendly.add(obj)
        if type[1][0]:
            self.unfriendly.add(obj)
            
        # Add an attack group (for combat)
        if type[0][1]:
            obj.attackgroups.append(self.friendly)
        if type[1][1]:
            obj.attackgroups.append(self.unfriendly)
        
        # If the game is currently engaged in a fight, add spawned objects to the blocked group
        if self.blocking:
            self.blockedobjects.add(obj)
        
        # Update the object's position
        self.camera.updateposition(obj)
        
    def remove(self,player):
        player.kill()                       # Remove from all the sprite groups

    def makeplayer(self,obj):
        # Remove control from the old player
        # self.player.controller = None
        # Give the player control
        obj.controller = self.controller
        # Mark the object internally as the player
        self.player = obj
        # Have the camera follow the object
        self.camera.player = obj

    def spawnplayer(self,obj):
        self.spawnobj(obj,COMBAT_PLAYER)
        self.makeplayer(obj)

    def updateloading(self, info):
        surf = pygame.display.get_surface()
        surf.fill((0,0,0))
        title = self.subtitle.render("Preparing Level: " + self.levelname, 1, (255,255,255))
        text = self.font.render("Loading: " + info, 1, (255,255,255))
        
        surf.blit(title,((self.screenx - title.get_width())/2,20))
        surf.blit(text,((self.screenx - text.get_width())/2,(self.screeny - text.get_height())/2))
        pygame.display.flip()

class MovableSprite(pygame.sprite.Sprite):
    """A self-loading sprite"""
    def __init__(self, filename):
        pygame.sprite.Sprite.__init__(self) #call Sprite initializer
        self.image, self.rect = load_image(filename)

class ScrollingBG:
    """this function provides an interface to move two backgrounds at once in paralax"""
    def __init__(self, gameinfo):
        
        self.gameinfo = gameinfo
        
        self.fgimage = MovableSprite(os.path.join('data','levels', self.gameinfo.levelname, "front.png"))
        self.bgimage = MovableSprite(os.path.join('data','levels', self.gameinfo.levelname,"back.png"))
        self.bdimage = MovableSprite(os.path.join('data','levels', self.gameinfo.levelname,"rear.png"))
        self.mask =    MovableSprite(os.path.join('data','levels', self.gameinfo.levelname,"mask.png"))
        self.ground = MovableSprite(os.path.join('data','levels', self.gameinfo.levelname,"ground.png"))
        
        # Stencil the static tiles on to the background
        for static in gameinfo.statictiles:
            stamp = load_frame(os.path.join('data','tiles', static[0]+".png"))
            if static[1]==1:
                dest = self.fgimage.image
            elif static[1]==0:
                dest = self.bgimage.image
            elif static[1]==-1:
                dest = self.bdimage.image
            
            for i in range(static[4]):
                for j in range(static[5]):
                    dest.blit(stamp,(static[2]+(i*stamp.get_width()),static[3]+(j*stamp.get_height())))
        
        # Setup the pygame array system to use Numeric
        pygame.surfarray.use_arraytype('numpy')

        # Copy the images' alpha values to numpy arrays
        self.ground.a = pygame.surfarray.array_alpha(self.ground.image)
        self.mask.a = pygame.surfarray.array_alpha(self.mask.image)
        
        # Compare the values of the alpha channel with 127 forming a bool array.
        # Basically a 1 bit mask, but in a numpy array.
        self.ground.a = (self.ground.a > 127)
        self.mask.a = (self.mask.a > 127)
        
        # Create a third mask for the whole environment for non-walkable characters.
        self.fgimage.a = logical_or(self.ground.a, self.mask.a)

        self.gameinfo.levelx = self.fgimage.rect.width
        self.gameinfo.levely = self.fgimage.rect.height
        
        if (self.fgimage.rect.width - self.gameinfo.screenx) > 0:
            self.xfactor = float(self.bdimage.rect.width - self.gameinfo.screenx) / (self.fgimage.rect.width - self.gameinfo.screenx)
        else:
            self.xfactor = 0
        if (self.fgimage.rect.height - self.gameinfo.screeny) > 0:
            self.yfactor = float(self.bdimage.rect.height - self.gameinfo.screeny) / (self.fgimage.rect.height - self.gameinfo.screeny)
        else:
            self.yfactor = 0
        
    def setposx(self, x):
        self.fgimage.rect.left = x
        self.ground.rect.left = x
        self.bgimage.rect.left = x
        self.bdimage.rect.left = x * self.xfactor
        
    def setposy(self, y):
        self.fgimage.rect.bottom = y
        self.bgimage.rect.bottom = y
        self.ground.rect.bottom = y
        self.bdimage.rect.bottom = ((y - self.gameinfo.screeny) * self.yfactor) + self.gameinfo.screeny
        
class Camera:
    """this functions moves and animates the background"""
    def __init__(self, gameinfo):
        self.background = gameinfo.background
        self.gameinfo = gameinfo
        self.player = gameinfo.player
        
        self.leftedge = 0
        self.rightedge = 0
        self.topedge = 0
        self.bottomedge = 0
        
        self.clearblock()
        
        self.ix = self.player.positionx
        self.iy = self.player.positiony
        self.x = self.player.positionx
        self.y = self.player.positiony
        
    def setblock(self,rect):
        if rect.left > 0:
            self.leftedge = rect.left
        if rect.right < self.gameinfo.levelx:
            self.rightedge = rect.right
        if rect.top > 0:
            self.topedge = rect.top
        if rect.bottom < self.gameinfo.levely:
            self.bottomedge = rect.bottom
        
    def clearblock(self):
        self.leftedge = 0
        self.rightedge = self.gameinfo.levelx
        self.topedge = 0
        self.bottomedge = self.gameinfo.levely
        
    def isonscreen(self, obj):
        return True
        # Figures out if the Sprite obj is onscreen (or near the screen)
        screenleft = -self.background.fgimage.rect.left - 300
        screenright = -self.background.fgimage.rect.left + self.gameinfo.screenx + 300
        screentop = -self.background.fgimage.rect.top - 300
        screenbottom = -self.background.fgimage.rect.top + self.gameinfo.screeny + 300
    
        if obj.positionx > screenleft and obj.positionx < screenright and obj.positiony > screentop and obj.positiony < screenbottom:
            return True
        return False
        
    def updateposition(self, object):
        object.rect.x = object.positionx + self.gameinfo.background.fgimage.rect.left
        object.rect.y = object.positiony + self.gameinfo.background.fgimage.rect.top
        
    def update(self):
        
        #self.ix -= cmp(self.ix,self.player.positionx) * int(abs(self.ix - self.player.positionx) / 20)
        #self.iy -= cmp(self.iy,self.player.positiony) * int(abs(self.iy - self.player.positiony) / 5)
        
        speedx = cmp(self.x,self.player.positionx) * int(abs(self.x - self.player.positionx) / 4)
        speedy = cmp(self.y,self.player.positiony) * int(abs(self.y - self.player.positiony) / 4)
        if abs(speedx) > 50:
            speedx = 50 * cmp(speedx,0)
        if abs(speedy) > 50:
            speedy = 50 * cmp(speedy,0)
        self.x -= speedx
        self.y -= speedy
        
        #self.x = (self.player.positionx - self.ix) + self.player.positionx
        #self.y = self.player.positiony
        #self.y = (self.player.positiony - self.iy) + self.player.positiony
        
        # Center Level around player
        if self.x >= (self.leftedge + (self.gameinfo.screenx / 2)) and self.x <= (self.rightedge - (self.gameinfo.screenx / 2)):
            self.background.setposx((self.gameinfo.screenx / 2)-self.x)
        elif self.x < (self.leftedge + (self.gameinfo.screenx / 2)):
            self.background.setposx(-self.leftedge)
        else:
            self.background.setposx(self.gameinfo.screenx-self.rightedge)
            
        if self.y >= (self.topedge + (self.gameinfo.screeny / 2)) and self.y <= (self.bottomedge - (self.gameinfo.screeny / 2)):
            self.background.setposy(self.gameinfo.levely + (self.gameinfo.screeny / 2) - self.y)
        elif self.y < (self.topedge + (self.gameinfo.screeny / 2)):
            self.background.setposy(self.gameinfo.levely - self.topedge)
        else:
            self.background.setposy(self.gameinfo.levely - self.bottomedge + self.gameinfo.screeny)

def main():
    """this function is called when the program starts.
       it initializes everything it needs, then runs in
       a loop until the function returns."""
    #Initialize Everything
    pygame.init()
    #pygame.mixer.init()
    
    # Gameinfo handles the screen size, starting positions, level name, background, camera, and more 
    # It reads out of data/conf.txt. If the file is missing info, defaults are used.
    gameinfo = GameInfo()
    
    levels = list()
    levelnumber = 0
    for directory in os.listdir(os.path.join('data','levels')):
        if os.path.isdir(os.path.join('data','levels', directory)) and \
            os.path.isfile(os.path.join('data','levels', directory, 'back.png')) and \
            os.path.isfile(os.path.join('data','levels', directory, 'front.png')) and \
            os.path.isfile(os.path.join('data','levels', directory, 'ground.png')) and \
            os.path.isfile(os.path.join('data','levels', directory, 'mask.png')) and \
            os.path.isfile(os.path.join('data','levels', directory, 'rear.png')) and \
            os.path.isfile(os.path.join('data','levels', directory, 'props.txt')):
            
            levels.append(("Load Level: " + directory, directory))
    
    clock = pygame.time.Clock()
    
    pygame.mixer.music.load(os.path.join('data','music','title.ogg'))
    pygame.mixer.music.play()
    
    selection = Menu("Project 906:","An Experimental Platformer", (("Start Game", 0),("Select Level", 1),("Quit Game", 2)), gameinfo).show()
    if selection == 1:
        gameinfo.levelname = Menu("Project 906:","An Experimental Platformer", levels, gameinfo).show()
    elif selection == 2:
        pygame.quit()
        return
    
    gameinfo.loadlevel()
	
#Main Loop
    while 1:
        clock.tick(gameinfo.framerate)

    # Process input from the keyboard and USB pads
        gameinfo.controller.update()
        
        # Update all conditions
        #for condition in gameinfo.conditions:
        #    condition.check(gameinfo)
        
        # Check to see if we tripped any triggers.
        for trigger in gameinfo.triggers:
            trigger.tryActivate(gameinfo)
            
        # Enforce blocking if it is currently enabled.            
        if gameinfo.blocking:
            for player in gameinfo.blockedobjects:
                if (player.positionx+player.padx/2) < gameinfo.block.left:
                    player.positionx = gameinfo.block.left-player.padx/2
                    player.speedx = 0
                if (player.positionx + player.rect.width - player.padx/2) > gameinfo.block.right:
                    player.positionx = gameinfo.block.right - player.rect.width + (player.padx/2)
                    player.speedx = 0
                if player.positiony < gameinfo.block.top:
                    player.positiony = gameinfo.block.top
                    player.speedy = 0
                if player.positiony > gameinfo.block.bottom:
                    player.die()
        
        # Check to see if the player pressed "Start"
        if gameinfo.controller.start_press and not(gameinfo.gameover):
            # Show the menu
            gameinfo.rhythmengine.pause()
            selection = Menu("Game Paused","", (("Return to Game", 0),("Return to Main Menu", 1),("Quit Game", 2)), gameinfo, bluranddim(pygame.display.get_surface())).show()
            if selection == 1:
                pygame.quit()
                main()
                return
            elif selection == 2:
                pygame.quit()
                return
            gameinfo.rhythmengine.unpause()
    
# Start Physics
        gameinfo.camera.update()
        
        # Process Physics Objects
        
        for player in gameinfo.physicsobjects:
            if gameinfo.camera.isonscreen(player) or player == gameinfo.player:
                
                # Process physics on the player
                player.physics()
                
                for atkgroup in player.attackgroups:
                    for attack in player.attackboxes:
                        for victim in atkgroup:
                            if victim != player and victim.dying == 0:
                                # Determine the victim's hitbox
                                vrect = victim.rect.inflate(victim.padx,victim.pady)
                                vrect.x = victim.positionx - (victim.padx/2)
                                vrect.y = victim.positiony - (victim.pady/2)
                                # Determine the attack's hitbox
                                arect = attack[1].move(player.positionx,player.positiony)
                                if vrect.colliderect(arect):
                                    victim.hit(attack[0], 30, math.atan2(arect.centery-vrect.centery,vrect.centerx-arect.centerx), 10)
                
                # Spawn in any children created by the current object
                player.spawner.spawnwaiting(gameinfo)
                
                # Check to see if the character should be dead from falling off the map
                if (player.positiony > gameinfo.levely + 100 or player.positiony < -500):
                    player.die()
                
                # Check to see if the character is totally dead
                if player.dead == 1:
                    if player == gameinfo.player:
                        if gameinfo.enablelives == 1:
                            gameinfo.lives -= 1
                        if gameinfo.lives > 0:
                            gameinfo.resetlevel()
                            player.respawn()
                        else:
                            gameinfo.gameover = True
                            gameinfo.gameovermenu = Menu("Game Over","Try Dying Less", (("Return to Main Menu", 0),("Quit Game", 1)), gameinfo, None)
                            gameinfo.hudicon.show(4)
                            pygame.mixer.music.stop()
                            gameinfo.remove(player)
                    else:
                        gameinfo.remove(player)
                        
                # Update the object's screen-relative position.
                gameinfo.camera.updateposition(player)
            
# End Physics
    
        # Check to see if the current player is dying.
        if gameinfo.player.dying == 1:
            gameinfo.hudicon.show(4)
            for subject in gameinfo.player.spawner.children:
                gameinfo.camera.player = subject
    
    # Start animated tiles / non-physics objects
        for anim in gameinfo.animobjs:
            gameinfo.camera.updateposition(anim)
    # End animated tiles / non-physics objects
    
    # Update the meter with the player's health.
        # setlevel jumps the meter right away. newlevel animates.
        #gameinfo.powermeter.newlevel(gameinfo.player.health)
        gameinfo.powermeter.setlevel(gameinfo.player.health)
        
    #Draw Frame
        gameinfo.allsprites.update()
        gameinfo.allsprites.draw(gameinfo.screen)
        
    # Draw the HUD
        gameinfo.powermeter.draw()
        gameinfo.hudicon.draw()
        gameinfo.rhythmengine.update()
        if gameinfo.enablelives:
            gameinfo.livesmeter.draw()
        
        # If the player is out of lives, throw up an effect over the screen
        # and draw the death menu.
        if gameinfo.gameover:
            gameinfo.gameovermenu.background = bluranddim(pygame.display.get_surface())
            selection = gameinfo.gameovermenu.overlay()
            if selection == 0:
                pygame.quit()
                main()
                return
            elif selection == 1:
                pygame.quit()
                return

    # Flip changes on to the screen
        pygame.display.flip()

#Game Over


#this calls the 'main' function when this script is executed
if __name__ == '__main__': main()